#include "hashmanger.h"
#include <Windows.h>
#include <iostream>
#include "QDebug"
#include "Windows.h"

//无子00 障碍01 黑子10 白子11
std::bitset<256> HashManger::turn_to_key(int *cb){
    std::bitset<256> bit;
    bit.reset();
    int m = 0;
    for (int i = 13;i <= 130;i+=12) {
        for (int j = i;j < i+10;j++ ) {
            switch (cb[j]) {
            case nochess:   {m+=2;break;}
            case blackh:    {m++;bit.set(m++);break;}
            case whiteh:    {m++;bit.set(m++);break;}
            case blackc:    {bit.set(m++);m++;break;}
            case whitec:    {bit.set(m++);bit.set(m++);break;}
            }
        }
    }
    return bit;
}

void HashManger::outfile(std::ofstream &out,const std::bitset<256> &key,const Score_struct &value){
    out.write((char*)&key,25);
    out.write((char*)&value.king,1);
    out.write((char*)&value.queen,1);
}

void HashManger::infile(std::ifstream &in, std::bitset<256> &key, Score_struct &value){
    in.read((char*)&key,25);
    in.read((char*)&value.king,1);
    in.read((char*)&value.queen,1);
}

Score_struct HashManger::find_score(Cb_with_num &cb){
    if(map_search.count(cb.chess_num) == 0) return Score_struct().set_invalid();
    Score_struct s;
    auto value = turn_to_key(cb.chessborad);
    i++;
    /*
    {
        std::lock_guard<std::mutex> lg(*lock[cb.chess_num]);
        old_use_num[cb.chess_num].second++;
    }
    */old_use_num[cb.chess_num].second++;
    if(map_search[cb.chess_num].old_table.find(value) != map_search[cb.chess_num].old_table.end()){
        s = map_search[cb.chess_num].old_table.at(value);
        old_use_num[cb.chess_num].first++;
    }
    else if(map_search[cb.chess_num].new_table.find(value) != map_search[cb.chess_num].new_table.end())
        s = map_search[cb.chess_num].new_table.at(value);
    else return Score_struct().set_invalid();
    shot++;
    if(cb.player == blackc) return s;
    else return Score_struct(-s.king,-s.queen);
}

std::string HashManger::make_table_path(int chess_num) const{
    return save_path + std::to_string(chess_num);
}

void HashManger::update(Cb_with_num &cb, Score_struct &s){
    if(map_search.count(cb.chess_num) == 0) return;
    std::lock_guard<std::mutex> lg(*lock.at(cb.chess_num));
    if(cb.player == blackc) map_search[cb.chess_num].new_table.insert({turn_to_key(cb.chessborad),s});
    else map_search[cb.chess_num].new_table.insert({turn_to_key(cb.chessborad),Score_struct(-s.king,-s.queen)});
}

void HashManger::save(int chess_num) {
    if(map_search[chess_num].new_table.size() == 0){
        return;
    }
    if(old_use_num[chess_num].first / old_use_num[chess_num].second > 0.6) return;
    qDebug() << "saving " + QString::number(chess_num);
    std::string path = make_table_path(chess_num);
    std::ifstream in(path,std::ios::binary);
    if(in.is_open()){
        in.seekg(0,std::ios::end);
        int size = in.tellg();
        in.close();
        //文件已存在但未达到150w棋盘数，添加30w张
        if(size < max_save_size){
            qDebug() << "append file";
            int rest_size = min(max_save_size - size,append_num * unit_size);
            int it = 0;
            std::ofstream out(path,std::ios::binary|std::ios::app);
            for(auto &i: map_search[chess_num].new_table){
                outfile(out,i.first,i.second);
                it += unit_size;
                if(it >= rest_size) break;
            }
            out.close();
            qDebug() << "append finished";
        }
        //文件已存在，更新10w张
        else if(if_update){
            int it = 0,k = 0,new_size = update_num * unit_size;
            std::ofstream out(path,std::ios::binary|std::ios::trunc);
            qDebug() << "saving new";
            for(auto &i: map_search[chess_num].new_table){
                outfile(out,i.first,i.second);
                it += unit_size;
                k++;
                if(it >= new_size) break;
            }
            qDebug() << "saving old";
            for(auto &i: map_search[chess_num].old_table){
                outfile(out,i.first,i.second);
                it += unit_size;
                k++;
                if(it >= max_save_size) break;
            }
            out.close();
            qDebug() << k* unit_size;
        }
        qDebug() << "saving finished " + QString::number(chess_num);
        return;
    }

    //文件不存在,添加30w张
    int it = 0;
    std::ofstream out(path,std::ios::binary|std::ios::app);
    int append_size = append_num * unit_size;
    for(auto &i: map_search[chess_num].new_table){
        outfile(out,i.first,i.second);
        it += unit_size;
        if(it >= append_size) break;
    }
    qDebug() << "save finished " + QString::number(chess_num);
    out.close();
}

void HashManger::load(int chess_num,bool will_del){
    if(map_search.count(chess_num)) return;
    Map_Set *map;
    {
        std::lock_guard<std::mutex> lg(load_lock);
        if(will_del){
            short will_delete = *now_cb.begin();
            save(will_delete);
            now_cb.erase(will_delete);
            load_finished.erase(will_delete);
            map_search.erase(will_delete);
            old_use_num.erase(will_delete);
            lock.erase(will_delete);
        }
        load_finished.insert({chess_num,false});
        now_cb.insert(chess_num);
        map_search.insert({chess_num,Map_Set()});
        old_use_num.insert({(char)chess_num,{0,0}});
        lock.insert({chess_num,std::make_shared<std::mutex>()});
        map = &map_search[chess_num];
    }
    std::string path = make_table_path(chess_num);
    std::ifstream in(path,std::ios::binary);
    std::bitset<256> key;
    Score_struct s;
    qDebug() << "loading step " + QString::number(chess_num);
    int load_size = get_load_size(chess_num);
    if(in.is_open()){
        for(int i = 0;i < load_size && in.good();i+=unit_size){
            infile(in,key,s);
            map->old_table.insert({key,s});
        }
        qDebug() <<"step " << chess_num <<" map_size:"<< map->old_table.size();
        in.close();
    }
    load_finished[chess_num] = true;
    qDebug() << "load finished " + QString::number(chess_num);
}

bool HashManger::is_finished(){
    for(auto i:load_finished)
        if(!i.second) return false;
    return true;
}

void HashManger::reset_hash(){
    map_search.clear();
}

